#include "common.h"
#include "em_cmu.h"
#include "em_gpio.h"

#include "elab_pin.h"
#include "bsp_pin.h"
#include "drv_util.h"
#define TAG "PIN_HW"

static elab_err_t _init(elab_pin_t * const me);
static elab_err_t _set_mode(elab_pin_t * const me, uint8_t mode);
static elab_err_t _get_status(elab_pin_t * const me, bool *status);
static elab_err_t _set_status(elab_pin_t * const me, bool status);

static const elab_pin_ops_t pin_driver_ops = {
    .init = _init,
    .set_mode = _set_mode,
    .get_status = _get_status,
    .set_status = _set_status,
};


int bsp_gpio_pin_init(elab_pin_driver_t *me, const char * name, const char* pin_name)
{

    if(!check_pin_name_valid(pin_name)) return -1;

    memset(&me->device.super, 0, sizeof(me->device.super));
    
    me->pin_name = pin_name;

    elab_pin_register(&me->device, name, &pin_driver_ops, me);

    return 0;
}





/* private functions -------------------------------------------------------- */
/**
  * @brief  The PIN driver initialization function.
  * @param  me  PIN device handle.
  * @retval None.
  */
static elab_err_t _init(elab_pin_t * const me)
{
    elab_assert(me != NULL);

    elab_pin_driver_t *driver = (elab_pin_driver_t *)me->super.user_data;
    uint32_t port = get_port_from_name(driver->pin_name);
    uint16_t pin = get_pin_from_name(driver->pin_name);
    elab_assert(port != -1);
     /* Configure GPIO pins */
    CMU_ClockEnable(cmuClock_GPIO, true);
    /* Configure GPIO pin. */
    GPIO_PinModeSet((GPIO_Port_TypeDef)port, pin, gpioModeDisabled, 1);
    
    return ELAB_OK;
}

/**
  * @brief  The PIN driver set_mode function.
  * @param  me  PIN device handle.
  * @retval None.
  */
static elab_err_t _set_mode(elab_pin_t * const me, uint8_t mode)
{
    elab_assert(me != NULL);
    elab_assert(mode < PIN_MODE_MAX);

    elab_pin_driver_t *driver = (elab_pin_driver_t *)me->super.user_data;
    uint32_t port = get_port_from_name(driver->pin_name);
    uint16_t pin = get_pin_from_name(driver->pin_name);
    elab_assert(port != -1);
    /* Configure GPIO pin. */
    GPIO_Mode_TypeDef gpio_mode;
    uint8_t level = 0;
    if (mode == PIN_MODE_INPUT)
    {
        gpio_mode = gpioModeInput;
    }
    else if (mode == PIN_MODE_INPUT_PULLUP)
    {
        gpio_mode = gpioModeInputPull;
        level = 1;
    }
    else if (mode == PIN_MODE_INPUT_PULLDOWN)
    {
        gpio_mode = gpioModeInputPull;
        level = 0;
    }
    else if (mode == PIN_MODE_OUTPUT_PP)
    {
        gpio_mode = gpioModePushPull;
        level = 0;
    }
    else if (mode == PIN_MODE_OUTPUT_OD)
    {
        gpio_mode = gpioModeWiredAnd;
        level = 0;
    }
    GPIO_PinModeSet((GPIO_Port_TypeDef)port, pin, gpio_mode, level);
    
    return ELAB_OK;
}

/**
  * @brief  The PIN driver set_mode function.
  * @param  me  PIN device handle.
  * @retval GPIO status.
  */
static elab_err_t _get_status(elab_pin_t * const me, bool *status)
{
    elab_assert(me != NULL);

    elab_pin_driver_t *driver = (elab_pin_driver_t *)me->super.user_data;
    uint32_t port = get_port_from_name(driver->pin_name);
    uint16_t pin = get_pin_from_name(driver->pin_name);

    int gpip_status = GPIO_PinInGet(port, pin);
    *status = (gpip_status == 1) ? true : false;
    
    return ELAB_OK;
}

/**
  * @brief  The PIN driver set_status function.
  * @param  me      PIN device handle.
  * @param  status  GPIO status.
  * @retval None.
  */
static elab_err_t _set_status(elab_pin_t * const me, bool status)
{
    elab_assert(me != NULL);
    
    elab_pin_driver_t *driver = (elab_pin_driver_t *)me->super.user_data;
    uint32_t port = get_port_from_name(driver->pin_name);
    uint16_t pin = get_pin_from_name(driver->pin_name);
    
    if(status)
    {
        GPIO_PinOutSet(port, pin);
    }
    else
    {
        GPIO_PinOutClear(port, pin);
    }
    return ELAB_OK;
}
